package com.example.coder_voucher.service;

import com.example.coder_voucher.entity.Voucher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.List;

@Service
public class VoucherInventoryService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private RedisScript<Long> claimVoucherScript;
    
    @Autowired
    private RedisScript<Long> rollbackVoucherScript;
    
    @Autowired
    private RedisScript<Long> initVoucherInventoryScript;
    
    private static final String VOUCHER_INVENTORY_PREFIX = "voucher:inventory:";
    private static final String USER_VOUCHERS_PREFIX = "user:vouchers:";
    private static final String USER_USED_VOUCHERS_PREFIX = "user:used:";
    
    /**
     * Initialize voucher inventory in Redis
     */
    public Long initVoucherInventory(Voucher voucher) {
        String inventoryKey = VOUCHER_INVENTORY_PREFIX + voucher.getId();
        List<String> keys = Arrays.asList(inventoryKey);
        List<String> args = Arrays.asList(
            voucher.getTotalQuantity().toString(),
            voucher.getClaimedQuantity().toString()
        );
        
        return redisTemplate.execute(initVoucherInventoryScript, keys, args.toArray());
    }
    
    /**
     * Claim voucher using Lua script
     */
    public VoucherClaimResult claimVoucher(Long userId, Voucher voucher) {
        String inventoryKey = VOUCHER_INVENTORY_PREFIX + voucher.getId();
        String userVouchersKey = USER_VOUCHERS_PREFIX + userId;
        
        List<String> keys = Arrays.asList(inventoryKey, userVouchersKey);
        List<String> args = Arrays.asList(
            voucher.getCode(),
            userId.toString(),
            String.valueOf(LocalDateTime.now().toEpochSecond(ZoneOffset.UTC)),
            String.valueOf(voucher.getStartTime().toEpochSecond(ZoneOffset.UTC)),
            String.valueOf(voucher.getEndTime().toEpochSecond(ZoneOffset.UTC))
        );
        
        Long result = redisTemplate.execute(claimVoucherScript, keys, args.toArray());
        return VoucherClaimResult.fromCode(result);
    }
    
    /**
     * Rollback voucher using Lua script
     */
    public VoucherRollbackResult rollbackVoucher(Long userId, Voucher voucher) {
        String inventoryKey = VOUCHER_INVENTORY_PREFIX + voucher.getId();
        String userVouchersKey = USER_VOUCHERS_PREFIX + userId;
        String userUsedVouchersKey = USER_USED_VOUCHERS_PREFIX + userId;
        
        List<String> keys = Arrays.asList(inventoryKey, userVouchersKey, userUsedVouchersKey);
        List<String> args = Arrays.asList(
            voucher.getCode(),
            userId.toString()
        );
        
        Long result = redisTemplate.execute(rollbackVoucherScript, keys, args.toArray());
        return VoucherRollbackResult.fromCode(result);
    }
    
    /**
     * Mark voucher as used
     */
    public void markVoucherAsUsed(Long userId, String voucherCode) {
        String userUsedVouchersKey = USER_USED_VOUCHERS_PREFIX + userId;
        redisTemplate.opsForSet().add(userUsedVouchersKey, voucherCode);
    }
    
    /**
     * Get current inventory from Redis
     */
    public Long getCurrentInventory(Long voucherId) {
        String inventoryKey = VOUCHER_INVENTORY_PREFIX + voucherId;
        Object inventory = redisTemplate.opsForValue().get(inventoryKey);
        return inventory != null ? Long.valueOf(inventory.toString()) : 0L;
    }
    
    /**
     * Sync database claimed quantity with Redis
     */
    public void syncInventoryWithDatabase(Voucher voucher) {
        Long redisInventory = getCurrentInventory(voucher.getId());
        Long expectedInventory = (long) (voucher.getTotalQuantity() - voucher.getClaimedQuantity());
        
        if (!redisInventory.equals(expectedInventory)) {
            initVoucherInventory(voucher);
        }
    }
    
    // Result classes
    public static class VoucherClaimResult {
        private final boolean success;
        private final String message;
        private final Long remainingInventory;
        
        private VoucherClaimResult(boolean success, String message, Long remainingInventory) {
            this.success = success;
            this.message = message;
            this.remainingInventory = remainingInventory;
        }
        
        public static VoucherClaimResult fromCode(Long code) {
            if (code == null) {
                return new VoucherClaimResult(false, "Unknown error: Redis operation failed", 0L);
            }
            
            switch (code.intValue()) {
                case -1:
                    return new VoucherClaimResult(false, "Voucher not started yet", 0L);
                case -2:
                    return new VoucherClaimResult(false, "Voucher expired", 0L);
                case -3:
                    return new VoucherClaimResult(false, "User has already claimed this voucher", 0L);
                case -4:
                    return new VoucherClaimResult(false, "Voucher not found in inventory", 0L);
                case -5:
                    return new VoucherClaimResult(false, "Voucher sold out", 0L);
                default:
                    return new VoucherClaimResult(true, "Voucher claimed successfully", code);
            }
        }
        
        public boolean isSuccess() { return success; }
        public String getMessage() { return message; }
        public Long getRemainingInventory() { return remainingInventory; }
    }
    
    public static class VoucherRollbackResult {
        private final boolean success;
        private final String message;
        private final Long currentInventory;
        
        private VoucherRollbackResult(boolean success, String message, Long currentInventory) {
            this.success = success;
            this.message = message;
            this.currentInventory = currentInventory;
        }
        
        public static VoucherRollbackResult fromCode(Long code) {
            if (code == null) {
                return new VoucherRollbackResult(false, "Unknown error: Redis operation failed", 0L);
            }
            
            switch (code.intValue()) {
                case -1:
                    return new VoucherRollbackResult(false, "User has not claimed this voucher", 0L);
                case -2:
                    return new VoucherRollbackResult(false, "Voucher has already been used, cannot rollback", 0L);
                default:
                    return new VoucherRollbackResult(true, "Voucher rolled back successfully", code);
            }
        }
        
        public boolean isSuccess() { return success; }
        public String getMessage() { return message; }
        public Long getCurrentInventory() { return currentInventory; }
    }
}